#pragma once

class vector2
{
public:
	float x, y;

	vector2();
	vector2( float _x, float _y);
	~vector2();

	vector2 operator + ( const vector2& vector ) const;
	vector2 operator - ( const vector2& vector ) const;
	vector2 operator * ( const vector2& vector ) const;
	vector2 operator * ( const float val ) const;
	vector2 operator / ( const vector2& vector ) const;
	vector2 operator / ( const float val ) const;

	vector2& operator += ( const vector2& vector );
	vector2& operator -= ( const vector2& vector );
	vector2& operator *= ( const vector2& vector );
	vector2& operator *= ( const float val );
	vector2& operator /= ( const vector2& vector );
	vector2& operator /= ( const float val );

	bool operator == ( const vector2& vector ) const;
	bool operator != ( const vector2& vector ) const;

	float dot( const vector2& vector );
	float dotabs( const vector2& vector );

	vector2 mid_point( const vector2& vec ) const;

	void make_ceil( const vector2& vector );
	void make_floor( const vector2& vector );

	vector2& operator = ( const vector2& vector );

	float operator [] ( const unsigned id );

	bool operator > ( const vector2& vector );
	bool operator < ( const vector2& vector );

	float length();
};
//-----------------------------------------------------------------------------
class vector3
{
public:
	float x, y, z;

	vector3();
	vector3( float _x, float _y, float _z );
	~vector3();

	vector3 operator + ( const vector3& vector ) const;
	vector3 operator - ( const vector3& vector ) const;
	vector3 operator * ( const vector3& vector ) const;
	vector3 operator * ( const float val ) const;
	vector3 operator / ( const vector3& vector ) const;
	vector3 operator / ( const float val ) const;

	vector3& operator += ( const vector3& vector );
	vector3& operator -= ( const vector3& vector );
	vector3& operator *= ( const vector3& vector );
	vector3& operator *= ( const float val );
	vector3& operator /= ( const vector3& vector );
	vector3& operator /= ( const float val );

	bool operator == ( const vector3& vector ) const;
	bool operator != ( const vector3& vector ) const;

	float dot( const vector3& vector );
	float dotabs( const vector3& vector );
	vector3 cross(const vector3& vec ) const;

	vector3 mid_point( const vector3& vec ) const;

	void make_ceil( const vector3& vector );
	void make_floor( const vector3& vector );

	vector3& operator = ( const vector3& vector );

	float &operator [] ( const unsigned id );

	bool operator > ( const vector3& vector );
	bool operator < ( const vector3& vector );

	float length();

	float normalize();
};
//-----------------------------------------------------------------------------
class vector4
{
public:
	float x, y, z, w;

	vector4();
	vector4( float _x, float _y, float _z, float w );
	vector4(const vector2 &v);
	vector4(const vector3 &v);
	~vector4();

	vector4 operator + ( const vector4& vector ) const;
	vector4 operator - ( const vector4& vector ) const;
	vector4 operator * ( const vector4& vector ) const;
	vector4 operator * ( const float val ) const;
	vector4 operator / ( const vector4& vector ) const;
	vector4 operator / ( const float val ) const;

	vector4& operator += ( const vector4& vector );
	vector4& operator -= ( const vector4& vector );
	vector4& operator *= ( const vector4& vector );
	vector4& operator *= ( const float val );
	vector4& operator /= ( const vector4& vector );
	vector4& operator /= ( const float val );

	bool operator == ( const vector4& vector ) const;
	bool operator != ( const vector4& vector ) const;

	float dot( const vector4& vector );
	float dotabs( const vector4& vector );

	vector4& operator = ( const vector4& vector );

	float operator [] ( const unsigned id );

	bool operator > ( const vector4& vector );
	bool operator < ( const vector4& vector );
};

//-----------------------------------------------------------------------------

class matrix3
{
public:
	matrix3();

	matrix3(float m00, float m10, float m20,
			float m01, float m11, float m21,
			float m02, float m12, float m22);
	matrix3( const matrix3& m );
	~matrix3();

	void identity();

	float* operator [] ( size_t n );
	const float* operator [] ( size_t n ) const;

	void operator = ( const matrix3& m );

	bool operator != ( const matrix3& m ) const;
	bool operator == ( const matrix3& m ) const;

	matrix3 concatenate( const matrix3& m ) const;

	matrix3 operator * ( float mul ) const;
	matrix3 operator * ( const matrix3& m ) const;
	vector3 operator * ( const vector3& v );
	matrix3 operator + ( const matrix3& m );
	matrix3 operator - ( const matrix3& m );

	bool to_euler_angles(float &a, float &b, float &c) const;

	void QDUDecomposition(matrix3& kQ, vector3& kD, vector3& kU) const;

private:
	float m_m[3][3];

	friend class matrix4;
};

//-----------------------------------------------------------------------------

class quaternion
{
public:
	quaternion();
	quaternion( const matrix3& m );
	quaternion( float angle, const vector3& axis );
	quaternion( float _w, float _x, float _y, float _z );
	~quaternion();

	void from_rotation_matrix( const matrix3& m );
	void to_rotation_matrix( matrix3& m ) const;

	void from_angle_axis ( float angle, const vector3& axis );
	void to_angle_axis ( float& angle, vector3& axis ) const;

	void identity();
	void normalize();

	quaternion operator + ( const quaternion& q ) const;
	quaternion operator - ( const quaternion& q ) const;
	quaternion operator * ( const quaternion& q ) const;
	quaternion operator * ( float scalar ) const;
	vector3 operator * ( const vector3 &v) const;

	quaternion& operator = ( const quaternion& q );

	bool operator == ( const quaternion& q ) const;
    bool operator != (const quaternion& q) const;

	void from_euler_angles(float theta_x, float theta_y, float theta_z);

	quaternion inverse() const;

	float w, x, y, z;
};
//-----------------------------------------------------------------------------
class matrix4
{
public:
	matrix4();

	matrix4( const matrix4& m );
	matrix4( float m00, float m01, float m02, float m03,
						float m10, float m11, float m12, float m13,
						float m20, float m21, float m22, float m23,
						float m30, float m31, float m32, float m33 );
	matrix4( float *matrix );
	~matrix4();

	void identity();

	matrix4 concatenate( const matrix4 &m2 ) const;
	vector3 transform_affine(  const vector3& v) const;

	bool is_affine(void) const;

	matrix4 operator * ( float mul ) const;
	matrix4 operator * ( const matrix4& m ) const;
	vector3 operator * ( const vector3& v ) const;
	matrix4 operator + ( const matrix4 &m ) const;
	matrix4 operator - ( const matrix4 &m ) const;

	bool operator == ( const matrix4& m ) const;
	bool operator != ( const matrix4& m ) const;

	void operator = ( const matrix4& m );

	float* operator [] ( size_t nrow );
	const float* operator [] ( size_t nrow ) const;

	matrix4 transpose() const;

	void decomposition(vector3& position, vector3& scale, quaternion& orientation) const;
	void make_transform(const vector3& position, const vector3& scale, const quaternion& orientation);

	matrix3 extract_matrix3() const;
	vector3 extract_translation() const;

	matrix4 inverse() const;

	private:
		float m_m[4][4];
};

//-----------------------------------------------------------------------------

enum side
{
	NO_SIDE,
	POSITIVE_SIDE,
	NEGATIVE_SIDE,
	BOTH_SIDE
};

class plane
{
public:
	plane();
	plane( const vector3& normal, float d );
	plane( float x, float y, float z, float d );
	~plane();

	void normalise();

	float get_distance( const vector3& point );
	side get_side( const vector3& point );
	side get_side( const vector3& centre, const vector3& half_size );

	vector3	normal;
	float	distance;
};

//-----------------------------------------------------------------------------

enum extent
{
	EXTENT_NULL,
	EXTENT_FINITE,
	EXTENT_INFINITE
};

class bounding_box
{
public:
	bounding_box();
	bounding_box( const vector3& min, const vector3& max );
	bounding_box( float mx, float my, float mz, float Mx, float My, float Mz );
	~bounding_box();

	void set( const vector3& min, const vector3& max );
	void set( float mx, float my, float mz, float Mx, float My, float Mz );

	void merge( const bounding_box& bbox );

	vector3 get_minimum() const;
	vector3 get_maximum() const;
	vector3 get_centre() const;
	vector3 get_half_size() const;
	vector3 get_size() const;

	void set_null();

	bool is_null() const;
	bool is_finite() const;
	bool is_infinite() const;

	bounding_box& operator = ( const bounding_box& bbox );

	void transform_affine( const matrix4& m );

private:
	vector3	m_min;
	vector3	m_max;
	extent	m_extents;
};